Skip to content

Implementation Plan: T5-P5-A3-WP1 Server-Side Recipe-Read Prohibition & Write-Target Boundary#4105

Merged
Trecek merged 5 commits into
developfrom
t5-p5-a3-wp1-provide-a-server-side-backend-agnostic-gate-fun/4034
Jun 13, 2026
Merged

Implementation Plan: T5-P5-A3-WP1 Server-Side Recipe-Read Prohibition & Write-Target Boundary#4105
Trecek merged 5 commits into
developfrom
t5-p5-a3-wp1-provide-a-server-side-backend-agnostic-gate-fun/4034

Conversation

@Trecek

@Trecek Trecek commented Jun 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

Add server-side defense-in-depth guards to _guards.py that replicate the recipe-read prohibition currently enforced only by the recipe_read_guard.py PreToolUse hook, and add write-target boundary checking for run_cmd. This makes both prohibitions HARD-enforced inside the MCP tool layer, backend-agnostic (works for Claude Code, Codex, and any future backend), and independent of hook scripts.

Closes #4034

Implementation Plan

Plan file: /home/talon/projects/autoskillit-runs/impl-20260613-020615-380722/.autoskillit/temp/make-plan/t5-p5-a3-wp1-server-side-recipe-read-guard_plan_2026-06-13_021500.md

🤖 Generated with Claude Code via AutoSkillit

Token Usage Summary

Step Model count uncached output cache_read peak_ctx turns cache_write time
plan* opus[1m] 1 90 26.9k 3.0M 136.8k 77 116.9k 16m 37s
verify* sonnet 1 1.3k 11.3k 476.4k 62.4k 26 41.5k 6m 27s
implement* MiniMax-M3 1 3.4M 11.7k 0 0 92 0 7m 3s
fix* sonnet 1 216 14.0k 2.2M 117.4k 75 96.5k 7m 5s
audit_impl* sonnet 1 54 12.8k 231.0k 60.3k 17 41.0k 7m 8s
prepare_pr* MiniMax-M3 1 194.9k 2.2k 0 0 14 0 50s
compose_pr* MiniMax-M3 1 178.7k 2.2k 0 0 12 0 49s
review_pr* sonnet 1 142 36.9k 991.3k 89.8k 42 70.1k 8m 51s
resolve_review* sonnet 1 327 24.6k 2.8M 104.5k 89 83.7k 10m 40s
Total 3.8M 142.5k 9.7M 136.8k 449.6k 1h 5m

* Step used a non-Anthropic provider; caching behavior may differ.

Token Efficiency

Step LoC Changed cache_read/LoC cache_write/LoC output/LoC
plan 0
verify 0
implement 292 0.0 0.0 40.0
fix 15 144832.0 6432.4 932.1
audit_impl 0
prepare_pr 0
compose_pr 0
review_pr 0
resolve_review 8 354473.6 10458.9 3078.0
Total 315 30927.9 1427.3 452.4

Model Usage Breakdown

Model steps uncached output cache_read cache_write time
opus[1m] 1 90 26.9k 3.0M 116.9k 16m 37s
sonnet 5 2.0k 99.6k 6.7M 332.7k 40m 12s
MiniMax-M3 3 3.7M 16.0k 0 0 8m 42s

Trecek and others added 5 commits June 13, 2026 02:35
…ry guards

Add defense-in-depth guards to server/_guards.py that replicate the
recipe-read prohibition currently enforced only by the recipe_read_guard
PreToolUse hook, and add write-target boundary checking for run_cmd.

This makes both prohibitions HARD-enforced inside the MCP tool layer,
backend-agnostic (works for Claude Code, Codex, and any future backend),
and independent of hook scripts.

- _check_recipe_read_prohibition(): denies reads of recipe/skill/agent
  files (cmd path) and autoskillit.recipe.* callables (callable path)
  in headless sessions
- _check_write_target_boundary(): denies run_cmd writes outside the
  configured allowed write prefixes (fail-open when unset)
- _derive_run_cmd_write_prefixes(): resolves the env-var precedence
  (PREFIXES plural takes precedence over PREFIX singular)
- Wire guards into run_cmd and run_python after _require_enabled()
- Parity test for RECIPE_READ_DENY_TRIGGER in tests/hooks/test_hook_sync.py
- New invariant test suites for run_cmd and run_python
- Smoke importability tests in tests/server/test_guards_module.py

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Inline _derive_run_cmd_write_prefixes() into guard call to satisfy
  _has_toplevel_except_exception arch check (assignment before try block
  violated the structural contract)
- Add AUTOSKILLIT_SESSION_TYPE=orchestrator to _headless fixtures so
  _require_orchestrator_or_higher passes before reaching new guards
- Bump tools_execution.py line limit exemption from 1130→1150
- Add 'server' to bash_write_targets cascade map (_guards.py now imports
  extract_bash_write_targets from core)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ards.py

_RECIPE_READ_CALLABLE_PATTERN has a `^` prefix anchor, so .match() is the
idiomatic method (implicitly anchors at start); using .search() with `^` is
semantically equivalent but misleading. Remove the now-redundant `^` from
the pattern definition and switch the call site to .match().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When AUTOSKILLIT_ALLOWED_WRITE_PREFIXES is unset, _check_write_target_boundary
silently returns None. Add a logger.debug() call so silent misconfiguration is
diagnosable in production logs without changing guard behavior.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rs scope

The debug log must emit within the bound_contextvars(tool="run_cmd") block
so it carries the run_cmd context. Calling logger.debug() in _guards.py fires
before that scope is entered, breaking the observability contract tested by
test_run_cmd_binds_tool_contextvar_and_calls_ctx_info.

Move the log to tools_execution.py inside the try/bound_contextvars block,
calling _derive_run_cmd_write_prefixes() once more to check (acceptable
overhead for a debug log path).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Trecek Trecek added this pull request to the merge queue Jun 13, 2026
Merged via the queue into develop with commit c9ed35e Jun 13, 2026
3 checks passed
@Trecek Trecek deleted the t5-p5-a3-wp1-provide-a-server-side-backend-agnostic-gate-fun/4034 branch June 13, 2026 10:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant